package edu.ubb.remeye.server.plugin;

import java.io.File;
import java.util.ArrayList;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.ServiceLoader;

import org.osgi.framework.Bundle;
import org.osgi.framework.BundleContext;
import org.osgi.framework.BundleException;
import org.osgi.framework.Constants;
import org.osgi.framework.InvalidSyntaxException;
import org.osgi.framework.ServiceReference;
import org.osgi.framework.launch.Framework;
import org.osgi.framework.launch.FrameworkFactory;

import edu.ubb.remeye.exception.InaccessibleServiceException;
import edu.ubb.remeye.exception.OSGIBundleInstallationException;
import edu.ubb.remeye.exception.OSGIFrameworkStartException;
import edu.ubb.remeye.exception.OSGIFrameworkStopException;
import edu.ubb.remeye.gui.internationalization.LanguageManager;
import edu.ubb.remeye.server.plugin.service.EventService;

/**
 * The plug-in manager class.
 * 
 * @author Andrew Ianicicau
 *
 */
public class PluginManager {

	///////////////////////////////////////////////////////////////////////////////////
	////		ATTRIBUTES - PluginManager
	///////////////////////////////////////////////////////////////////////////////////

	/**
	 * Instance of this class for singleton design pattern.
	 */
	private static PluginManager manager;
	
	/**
	 * OSGi framework.
	 */
	private Framework osgiFramework;
	
	/**
	 * Path of plug-ins to install.
	 */
	private final String PLUGINS_PATH = "plugins";
	
	/**
	 * List of plug-in files.
	 */
	private List<File> pluginFiles;
	
	/**
	 * Bundle context.
	 */
	private BundleContext context;

	
	
	///////////////////////////////////////////////////////////////////////////////////
	////		CONSTRUCTORS - PluginManager
	///////////////////////////////////////////////////////////////////////////////////

	/**
	 * Gets the plug-in files and gets the OSGI framework.
	 */
	private PluginManager() {
		// get plug-in files
		pluginFiles = getPluginFiles();
		
		// set properties for framework
		Map properties = new Hashtable();
		properties.put(Constants.FRAMEWORK_STORAGE_CLEAN, Constants.FRAMEWORK_STORAGE_CLEAN_ONFIRSTINIT);
		properties.put(Constants.FRAMEWORK_SYSTEMPACKAGES_EXTRA, "edu.ubb.remeye.server.plugin.service");
		
		// get osgi framework
		osgiFramework = getFrameworkFactory().newFramework(properties);
	}

	
	
	///////////////////////////////////////////////////////////////////////////////////
	////		METHODS - PluginManager
	///////////////////////////////////////////////////////////////////////////////////
	
	/**
	 * Returns the only instance of this classes object.
	 */
	public static PluginManager getInstance() {
		if(manager == null)
			manager = new PluginManager();
		return manager;
	}
	
	/**
	 * Start the framework and install the bundles.
	 * @throws OSGIFrameworkStartException 
	 * @throws OSGIBundleInstallationException 
	 * @throws BundleException 
	 */
	public void startManager() throws OSGIFrameworkStartException, OSGIBundleInstallationException {
		try {
			osgiFramework.start();
			context = osgiFramework.getBundleContext();
		} catch (BundleException e) {
			String errorMessage = LanguageManager.getLanguageManager().getText("pluginstarterror");
			throw new OSGIFrameworkStartException(errorMessage);
		}
		
		try {
			installAvailableBundles();
		} catch (BundleException e) {
			String errorMessage = LanguageManager.getLanguageManager().getText("bundleinstallerror");
			throw new OSGIBundleInstallationException(errorMessage);
		}
	}
	
	/**
	 * Stop the framework.
	 * @throws OSGIFrameworkStopException 
	 */
	public void stopManager() throws OSGIFrameworkStopException {
		try {
			osgiFramework.stop();
			osgiFramework.waitForStop(0);
		} catch (BundleException e) {
			String errorMessage = LanguageManager.getLanguageManager().getText("pluginstoperror");
			throw new OSGIFrameworkStopException(errorMessage);
		} catch (InterruptedException e) {
			String errorMessage = LanguageManager.getLanguageManager().getText("pluginstoperror");
			throw new OSGIFrameworkStopException(errorMessage);
		}
	}
	
	/**
	 * Returns the plug-in files from the specified path.
	 * @return List of plug-in files.
	 */
	private List<File> getPluginFiles() {
		File path = new File(PLUGINS_PATH);
		File[] files = path.listFiles();
		List<File> plugins = new ArrayList<File>();
		for(File f : files) {
			if(isPluginFile(f))
				plugins.add(f);
		}
		return plugins;
	}
	
	/**
	 * Determines whether a file is a plug-in or not.
	 * @param file
	 * @return
	 */
	private boolean isPluginFile(File file) {
		String extension = "jar";
		if (file.getName().toLowerCase().endsWith(extension))
	        return true;
		return false;
	}
	
	/**
	 * Returns the osgi framework factory.
	 * @return Framework factory.
	 */
	private FrameworkFactory getFrameworkFactory() {
		ServiceLoader<FrameworkFactory> factoryLoader = ServiceLoader.load(FrameworkFactory.class);
		Iterator<FrameworkFactory> iterator = factoryLoader.iterator();
		return iterator.next();
	}
	
	/**
	 * Installs a plug-in.
	 * @param file The plug-in file.
	 * @throws BundleException 
	 */
	private void installBundle(File file) throws BundleException {
		Bundle bundle = context.installBundle(file.toURI().toString());
		bundle.start();
	}
	
	/**
	 * Installs all the plug-ins from the plug-in files list.
	 * @throws BundleException 
	 */
	private void installAvailableBundles() throws BundleException {
		for(File file : pluginFiles)
			installBundle(file);
	}
	
	/**
	 * Returns the event service from the frameworks installed plug-ins with 
	 * operating system filtering.
	 * @return Event service.
	 * @throws InaccessibleServiceException 
	 */
	public EventService getEventService() throws InaccessibleServiceException {
		EventService service = null;
		String osFilter = "(" + EventService.OS_NAME_PROPERTY + "=" + System.getProperty("os.name") + ")";
		try {
			ServiceReference serviceReference[] = context.getServiceReferences(EventService.class.getName(), osFilter);
			if(serviceReference != null)
				service = (EventService) context.getService(serviceReference[0]);
		} catch (InvalidSyntaxException e) {
			String errorMessage = LanguageManager.getLanguageManager().getText("serviceerror");
			throw new InaccessibleServiceException(errorMessage);
		}
		return service;
	}

}
